package com.zhuhjay.framework.handler;

import com.zhuhjay.common.base.enums.HttpCode;
import com.zhuhjay.common.base.exception.ApiLimiterException;
import com.zhuhjay.common.base.exception.BasicBusinessException;
import com.zhuhjay.common.base.exception.ServerErrorException;
import com.zhuhjay.common.base.model.vo.HttpResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Set;

import static com.zhuhjay.common.base.constant.ApplicationConstant.BASE_PACKAGE;

/**
 * @author ZhuHJay
 * @date 2023/2/25 1:03
 */
@Slf4j
@RestControllerAdvice(BASE_PACKAGE)
public class GlobalExceptionHandler {

    // -----        系统异常        -----

    /** 权限校验异常 */
    @ExceptionHandler(AccessDeniedException.class)
    public HttpResult<?> handler(AccessDeniedException e) {
        return HttpResult.fail("操作权限不足");
    }

    /** 用于捕获@RequestBody类型参数触发校验规则抛出的异常 **/
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public HttpResult<?> handle(MethodArgumentNotValidException e) {
        StringBuilder sb = new StringBuilder();
        List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
        for (ObjectError error : allErrors) {
            sb.append(error.getDefaultMessage()).append(";");
        }
        return HttpResult.fail(sb.toString());
    }

    /** 用于捕获@RequestParam/@PathVariable参数触发校验规则抛出的异常 **/
    @ExceptionHandler(value = ConstraintViolationException.class)
    public HttpResult<?> handle(ConstraintViolationException e) {
        StringBuilder sb = new StringBuilder();
        Set<ConstraintViolation<?>> conSet = e.getConstraintViolations();
        for (ConstraintViolation<?> con : conSet) {
            String message = con.getMessage();
            sb.append(message).append(";");
        }
        return HttpResult.fail(sb.toString());
    }

    /** 用于捕获表单数据绑定 JavBean 时校验出现的异常 **/
    @ExceptionHandler(BindException.class)
    public HttpResult<?> handleValidation(BindException e) {
        StringBuilder sb = new StringBuilder();
        List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
        for (ObjectError error : allErrors) {
            sb.append(error.getDefaultMessage()).append(";");
        }
        return HttpResult.fail(sb.toString());
    }

    /** 请求媒体类型不受支持 **/
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public HttpResult<?> handle(HttpMediaTypeNotSupportedException e) {
        return HttpResult.fail("请求媒体类型不支持");
    }

    /** 请求方式不支持 **/
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public HttpResult<?> handle(HttpRequestMethodNotSupportedException e) {
        return HttpResult.fail(e.getMessage());
    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    public HttpResult<?> handle(HttpMessageNotReadableException e) {
        return HttpResult.fail("请正确携带参数");
    }

    // -----        自定义异常       -----

    @ExceptionHandler(ServerErrorException.class)
    public HttpResult<?> handle(ServerErrorException e) {
        log.error("自定义抛出的服务器异常: {}", e.getMessage());
        return HttpResult.fail(HttpCode.SERVER_ERROR);
    }

    /** 自定义异常，统一收集 **/
    @ExceptionHandler({BasicBusinessException.class, ApiLimiterException.class})
    public HttpResult<?> handle(RuntimeException e) {
        return HttpResult.fail(e.getMessage());
    }

    // -----        未知异常        -----

    @ExceptionHandler(Exception.class)
    public HttpResult<?> handle(Exception e) {
        log.error("未察觉的服务器异常", e);
        return HttpResult.fail(HttpCode.SERVER_ERROR);
    }

}
